package com.apps.saspect.impl;

import com.apps.sannotation.keys.TimeRangeKey;
import com.apps.saspect.CallTimeRangeLock;
import com.apps.saspect.model.vo.KeyValueVo;
import com.apps.saspect.utils.AspectUtils;
import com.apps.sconfig.SDevFastConfig;
import com.apps.sconfig.enums.LockType;
import com.apps.sexctption.DataException;
import com.apps.sexctption.LockException;
import com.apps.sexctption.NullException;
import com.apps.stoolkit.RedissonToolkit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
@Configuration
public class CallTimeRangeLockAspect {

    @Autowired(required = false)
    private SDevFastConfig sDevFastConfig;

    @Autowired(required = false)
    private RedisTemplate<String,Object> redisTemplate;

    //锁前缀
    private String prifix="CallTimeRangeLockAspect_";


    @Pointcut("@annotation(com.apps.saspect.CallTimeRangeLock)")
    public void pointcut() {

    }

    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        CallTimeRangeLock callTimeLock =method.getDeclaredAnnotation(CallTimeRangeLock.class);
        if(null == callTimeLock)
            throw new NullException("没有找到@CallTimeRangeLock注解");
        String keys =prifix + joinPoint.getTarget().getClass().getSimpleName()+"_"+method.getName() + "_";
        List<KeyValueVo> params= AspectUtils.handlerParameter(joinPoint, TimeRangeKey.class); //获取方法的参数传值
        //用参数拼装key
        keys += AspectUtils.generateKey(params);
        //为保障操作的原子性必须先加锁再进行操作
        if(sDevFastConfig.getLockType() == LockType.LOCAL)
        {  //本地锁模式
            throw new DataException("暂时不支持本地锁");
//            if(!SpiderLockToolkit.tryLock(keys,callTimeLock.waittime(),600L,TimeUnit.SECONDS))
//                throw new LockException("系统繁忙,请稍后再试");
//            Object num =  redisTemplate.opsForValue().get(keys+"numbers");
//            //如果key还不存在则写入并设置过期时间
//            if(null == num)
//                redisTemplate.opsForValue().set(keys,1,callTimeLock.range(),TimeUnit.SECONDS);
//            //已存在则修改值
//            Integer value = (Integer) num;
//            if(value.intValue() >= callTimeLock.num())
//                throw new DataException(callTimeLock.errMsg());
//            redisTemplate.opsForValue().set(keys,value.intValue()+1);
//            SpiderLockToolkit.unlock(keys);
        }else if(sDevFastConfig.getLockType() == LockType.REDISSON)
        { //redis锁
            RLock lock = RedissonToolkit.getRLock(keys);
            boolean bs = lock.tryLock(callTimeLock.waittime(),TimeUnit.SECONDS);
            if(!bs)
                throw new LockException("系统繁忙,请稍后再试");
            keys+="numbers";
            Object num =  redisTemplate.opsForValue().get(keys);
            //如果key还不存在则写入并设置过期时间
            if(null == num)
                redisTemplate.opsForValue().set(keys,1,callTimeLock.range(),TimeUnit.SECONDS);
            else {
                //已存在则修改值
                Integer value = (Integer) num;
                if (value.intValue() >= callTimeLock.num()) {
                    //先释放锁
                    if (null != lock && (lock.isLocked() && lock.isHeldByCurrentThread()))
                        lock.unlock();
                    throw new DataException(callTimeLock.errMsg());
                }
                redisTemplate.opsForValue().set(keys, value.intValue() + 1);
            }
            //释放锁
            if (null != lock && (lock.isLocked() && lock.isHeldByCurrentThread()))
                lock.unlock();
        }
    }

}
